package com.xlh.service.courseTest.impl;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson.JSONArray;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.xlh.constant.CourseTestConstant;
import com.xlh.dao.courseTest.*;
import com.xlh.dto.PageDTO;
import com.xlh.dto.course.CourseBasicDTO;
import com.xlh.dto.courseTest.*;
import com.xlh.dto.courseTest.model.ModelIncludeChapterDTO;
import com.xlh.dto.courseTest.model.ModelNotChapterDTO;
import com.xlh.dto.courseTest.paper.PaperDTO;
import com.xlh.dto.courseTest.paper.PaperDetailDTO;
import com.xlh.dto.courseTest.paper.StudentPaperDTO;
import com.xlh.dto.courseTest.paper.TeacherPaperDTO;
import com.xlh.dto.user.UserInfoDTO;
import com.xlh.enums.FileTypeEnum;
import com.xlh.enums.LocalResourcesEnum;
import com.xlh.enums.courseTest.*;
import com.xlh.exception.CourseTestExceptionInfo;
import com.xlh.exception.common.GlobalException;
import com.xlh.listener.EndCourseTestListener;
import com.xlh.listener.EndCourseTestTask;
import com.xlh.param.courseTest.PaperParam;
import com.xlh.param.courseTest.PreviewPaperParam;
import com.xlh.param.courseTest.PreviewQuestionParam;
import com.xlh.param.courseTest.TestStartParam;
import com.xlh.pojo.course.ChapterBasic;
import com.xlh.pojo.course.Course;
import com.xlh.pojo.courseTest.*;
import com.xlh.pojo.user.Class;
import com.xlh.pojo.user.User;
import com.xlh.pojo.user.UserClass;
import com.xlh.properties.PandocProperties;
import com.xlh.service.course.ChapterService;
import com.xlh.service.course.CourseService;
import com.xlh.service.courseTest.*;
import com.xlh.service.file.FileService;
import com.xlh.service.user.ClassService;
import com.xlh.service.user.UserService;
import com.xlh.util.BeanUtil;
import com.xlh.util.ExcelsUtil;
import com.youbenzi.md2.util.MDUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.task.TaskRejectedException;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author zhr 2019/2/20
 */
@Service
//@DependsOn(value = {"SpringContextUtil"})
public class PaperServiceImpl implements PaperService {

    @Autowired
    private QuestionRepositoryService questionRepositoryService;
    @Autowired
    private QuestionRepositoryDetailsService questionRepositoryDetailsService;
    @Autowired
    private PaperModelService paperModelService;
    @Autowired
    private UserService userService;
    @Autowired
    @Lazy
    private PaperAnswerSheetService answerSheetService;
    @Autowired
    private CourseService courseService;
    @Autowired
    private PaperQuestionService questionService;
    @Autowired
    private PaperAnswerService answerService;
    @Autowired
    private ClassService classService;
    @Autowired
    private UserClassService userClassService;
    @Autowired
    private ExperimentService experimentService;
    @Autowired
    private AnswerSheetExperimentService answerSheetExperimentService;
    @Autowired
    private ChapterService chapterService;
    @Autowired
    private CourseTestPaperAnswerSheetMapper courseTestPaperAnswerSheetMapper;
    @Autowired
    private CourseTestPaperMapper paperMapper;
    @Autowired
    private CourseTestPaperMapperExt paperMapperExt;
    @Autowired
    private CourseTestPaperQuestionMapper courseTestPaperQuestionMapper;
    @Autowired
    private CourseTestPaperClassMapper courseTestPaperClassMapper;
    @Autowired
    private FileService fileService;
    @Autowired
    protected PandocProperties pandocProperties;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private static ConcurrentHashMap<Long, String> lockQuee = new ConcurrentHashMap<>();
    @Value("${resources.root-dir}")
    private String localMappeing;
    @Autowired
    private CourseTestPaperTemplateService courseTestPaperTemplateService;
    @Autowired
    private CourseTestPaperClassService courseTestPaperClassService;

    private static Logger logger = LoggerFactory.getLogger(PaperServiceImpl.class);

    // 默认30秒之后
    private static TimedCache<Long, String> papaerIsHavePackage = CacheUtil.newTimedCache(900000);

    private static Map<Long, CourseTestPaper> paperStatusMap = Maps.newConcurrentMap();

    private static Map<Long, CourseTestPaper> paperCacheMap = Collections.synchronizedMap(new WeakHashMap<>());

    @Override
    public PageDTO<PaperListDataDTO> listPaper(Integer pageNum, Integer pageSize, Long courseId) {
        Course course = courseService.getCourseById(courseId);
        Long userId = course.getUserId();

        PageHelper.startPage(pageNum, pageSize);
        // 教师用户直接从course_test_paper表关联翻译表（class）即可
        List<PaperListDataDTO> list = paperMapperExt.selectPaperList(userId, courseId);

        // 设置试卷状态文字说明
        list.forEach(dto -> dto.setPaperStatusMessage(PaperStatusEnum.forCode(dto.getPaperStatus()).getMessage()));
        courseTestPaperClassService.setClassName(list);
        PageInfo<PaperListDataDTO> pageInfo = new PageInfo<>(list);
        return new PageDTO<>(pageInfo);
    }

    @Override
    public List<PaperListDataDTO> listPaper(Long userId, Long courseId) {
        // 学生用户从答题卡表反查试卷
        List<PaperListDataDTO> list = paperMapperExt.selectByStudentIdCourseId(userId, courseId);
        if (CollectionUtils.isEmpty(list)) {
            return list;
        }

        List<Long> paperIds = list.stream().map(PaperListDataDTO::getPaperId).collect(Collectors.toList());
        List<CourseTestPaperQuestion> firstQuestions = questionService.selectFirstQuestionByPaperIds(paperIds);
        Map<Long, CourseTestPaperQuestion> paperIdFirstQuestionMap = BeanUtil.mapByKey("paperId", firstQuestions);

        list.forEach(dto -> {
            PaperStatusEnum paperStat = PaperStatusEnum.forCode(dto.getPaperStatus());

            // 考试未结束，就需要创建答题卡对象
            // 考试若已结束，那就不再添加答题卡对象
            // 随后，需要对每个答题卡对象补充状态数据
            if (paperStat.notEnd()) {
                if (dto.getAnswerSheetId() == null) {
                    CourseTestPaperAnswerSheet answerSheet = answerSheetService.insertAndReturn(userId, dto.getPaperId());
                    dto.setAnswerSheetId(answerSheet.getId());
                    dto.setAnswerSheetStatus(answerSheet.getPaperStatus());
                }

                CourseTestPaperQuestion firstQuestion = paperIdFirstQuestionMap.get(dto.getPaperId());
                setPaperStat(dto, paperStat, firstQuestion);
            } else {
                if (dto.getAnswerSheetId() != null) {
                    CourseTestPaperQuestion firstQuestion = paperIdFirstQuestionMap.get(dto.getPaperId());
                    setPaperStat(dto, paperStat, firstQuestion);
                }
            }
        });

        // 过滤不包含答题卡信息的数据记录
        list = list.stream().filter(dto -> dto.getAnswerSheetId() != null).collect(Collectors.toList());

        return list;
    }

    private void setPaperStat(PaperListDataDTO dto, PaperStatusEnum paperStat, CourseTestPaperQuestion firstQuestion) {
        StudentStatusEnum statusEnum = StudentStatusEnum.getStudentStatusEnum(
                AnswerSheetStatusEnum.forCode(dto.getAnswerSheetStatus()), paperStat
        );
        // 如果用户的试卷还正在考试,查询试卷第一题是否为实验题,如果是实验题,强制将试卷状态变为实验题
        if (statusEnum.isTesting() && QuestionTypeEnum.isExperiment(firstQuestion.getType())) {
            statusEnum = StudentStatusEnum.EXPERIMENT_TEST;
        }
        dto.setPaperStatus(statusEnum.getStatus());
        dto.setPaperStatusMessage(statusEnum.isTesting() ? "正在考试" : statusEnum.getMessage());
        dto.setIsTesting(statusEnum.isTesting());
    }

    @Override
    public void put(CourseTestPaper paper) {
        paperStatusMap.put(paper.getId(), paper);
    }

    @Override
    public CourseTestPaper get(Long id) {
        return paperStatusMap.get(id) == null ? selectPaperById(id) : paperStatusMap.get(id);
    }

    @Override
    public void remove(Long id) {
        paperStatusMap.remove(id);
    }

    @Override
    @Transactional
    public void deletePaper(Long paperId, Long courseId) {
        Course course = courseService.getCourseById(courseId);
        Long userId = course.getUserId();

        // 删除前判断：该试卷是否已经开始考试，该试卷是否为本人生成
        CourseTestPaper courseTestPaper = paperMapper.selectByPrimaryKey(paperId);
        // 判断试卷是否为本人生成的
        if (!Objects.equal(userId, courseTestPaper.getTeacherId())) {
            throw new GlobalException(CourseTestExceptionInfo.NO_PERMIT_DELETE);
        }
        // 判断试卷是否已经开始考试
        if (!Objects.equal(courseTestPaper.getPaperStatus(), PaperStatusEnum.NOT_TEST.getCode())) {
            throw new GlobalException(CourseTestExceptionInfo.PAPER_IS_USED);
        }

        // 删除操作：先删除从表数据，再删除主表数据
        deletePaperInfo(paperId);
    }

    @Override
    @Transactional
    public void startTest(Long courseId, Long paperId, TestStartParam param) {
        Course course = courseService.getCourseById(courseId);
        Long userId = course.getUserId();

        CourseTestPaper paper = paperMapper.selectByPrimaryKey(paperId);
        // 判断试卷是否为本人生成的
        if (!Objects.equal(userId, paper.getTeacherId())) {
            throw new GlobalException(CourseTestExceptionInfo.NO_PERMIT_USE);
        }

        List<Long> classIds = param.getClassIds();
        List<Long> userLists = new ArrayList<>();

        // 更改试卷考试班级、试卷状态、结束考试时间
        updatePaperInfo(paper, classIds, param, PaperStatusEnum.TESTING.getCode(), paper.getTimeLength());

        for (int i = 0; i < classIds.size(); i++) {

            Long classId = classIds.get(i);
            // 判断选定班级是否有正在进行的考试
            if (classIsTesting(classId)) {
                throw new GlobalException("当前选择班级正在考试,无法同时进行考试");
            }
            // 获取班级下所有学生id
            List<Long> userIds = userService.getClassUser(classId);
            userLists.addAll(userIds);
        }

        // 插入答题卡信息
        answerSheetService.insertNewAnswerSheet(paperId, userLists);
        // 向考试状态监控队列中添加监控任务
        EndCourseTestListener.putTask(new EndCourseTestTask(paperId, paper.getEndTimeOrDate()));
    }

    private boolean classIsTesting(Long classId) {
        CourseTestPaperExample example = new CourseTestPaperExample();
        example.createCriteria().andEndTimeGreaterThanOrEqualTo(DateTime.now());
        List<CourseTestPaper> courseTestPapers = paperMapper.selectByExample(example);
        // 正在考试的试卷
        List<Long> paperIds = courseTestPapers.stream().map(CourseTestPaper::getId).collect(Collectors.toList());
        Boolean classIsTesting = courseTestPaperClassService.isContains(paperIds, classId);
        return classIsTesting;
    }

    @Override
    public PaperDTO paperDetails(Boolean isTeacher, Long userId, Long paperId, Long questionId, Long courseId) {
        return isTeacher ? teacherPaperDetails(paperId) : studentPaperDetails(userId, paperId, questionId, courseId);
    }

    /**
     * @param paperId 试卷id
     * @return 教师端查看试卷页面数据
     */
    private TeacherPaperDTO teacherPaperDetails(Long paperId) {
        // 查询试卷基本信息
        CourseTestPaper courseTestPaper = paperMapper.selectByPrimaryKey(paperId);

        // 查询试卷题目信息
        List<CourseTestPaperQuestion> questionList = questionService.selectByPaperId(paperId);
        Map<Integer, List<CourseTestPaperQuestion>> questionListTypeMap = BeanUtil.aggByKeyToList("type", questionList);

        List<CourseTestPaperQuestion> choiceQuestionList = questionListTypeMap.get(QuestionTypeEnum.CHOICE.getCode());
        Map<Long, List<CourseTestPaperAnswer>> choiceListQuestionIdMap = Maps.newHashMap();
        // 查询题目选项及答案信息
        if (CollectionUtils.isNotEmpty(choiceQuestionList)) {
            List<Long> choiceQuestionIdList = choiceQuestionList.stream().map(CourseTestPaperQuestion::getId).collect(Collectors.toList());
            List<CourseTestPaperAnswer> choiceList = answerService.selectAnswerList(choiceQuestionIdList, paperId);
            choiceListQuestionIdMap = BeanUtil.aggByKeyToList("questionId", choiceList);
        }

        // 封装结果集
        TeacherPaperDTO teacherPaperDTO = BeanUtil.transform(TeacherPaperDTO.class, courseTestPaper);
        if (teacherPaperDTO != null) {
            teacherPaperDTO.setContent(convert2PaperTypeDTO(questionListTypeMap, choiceListQuestionIdMap));
        }

        return teacherPaperDTO;
    }

    /**
     * @param paperId 试卷id
     * @return 当前试卷的剩余考试时间（单位：s）
     */
    @Override
    public Long getTimeLeft(Long paperId) {
        // 查询试卷基本信息
        CourseTestPaper courseTestPaper = selectByPrimaryKey(paperId);
        return DateUtil.between(DateTime.now(), courseTestPaper.getEndTime(), DateUnit.SECOND, false);
    }

    private CourseTestPaper selectByPrimaryKey(Long paperId) {
        return paperCacheMap.computeIfAbsent(paperId, paper -> paperMapper.selectByPrimaryKey(paperId));
    }

    @Override
    public void updatePaper(CourseTestPaper paper) {
        paperMapper.updateByPrimaryKeySelective(paper);
    }

    @Override
    public void readPaper(Long answerSheetId, List<TypeReadingDTO> typeReadingList) {
        // 将批阅参数转为答题卡参数
        List<AnswerSheetTypeDTO> answerSheetTypeList = convert2AnswerSheetTypeDTO(typeReadingList);

        // 查询答题卡信息
        CourseTestPaperAnswerSheet answerSheet = answerSheetService.getAnswerSheet(answerSheetId);

        // 提取答题卡详情，并提取为题目id-答题卡映射
        List<AnswerSheetTypeDTO> answerSheetTypeDTOList = JSONArray.parseArray(answerSheet.getContent(), AnswerSheetTypeDTO.class);
        List<AnswerSheetDTO> answerSheetList = AnswerSheetDTO.parse(answerSheetTypeList);
        Map<Long, Integer> idScoreMap = answerSheetList.stream().collect(Collectors.toMap(
                AnswerSheetDTO::getId, AnswerSheetDTO::getScore
        ));
        // 查询出试卷中的考试分数

        Set<Long> longs = idScoreMap.keySet();
        // 1. 校验是否有分值超出试题的分数
        if(CollectionUtil.isNotEmpty(longs)){

            CourseTestPaperQuestionExample questionExample = new CourseTestPaperQuestionExample();
            questionExample.createCriteria().andIdIn(Lists.newArrayList(longs));
            List<CourseTestPaperQuestion> courseTestPaperQuestions = courseTestPaperQuestionMapper.selectByExample(questionExample);
            Map<Long, Integer> idMapScore = courseTestPaperQuestions.stream().collect(Collectors.toMap(
                    CourseTestPaperQuestion::getId, CourseTestPaperQuestion::getScore
            ));

            for (Long id : idScoreMap.keySet()) {

                Integer mark = idScoreMap.get(id);
                Integer fullMark = idMapScore.get(id);

                if(fullMark == null){
                    continue;
                }
                if(mark > fullMark){
                    throw new GlobalException("评分不得大于满分！");
                }
            }
        }
        // 将得分信息封装至答题卡中
        answerSheetTypeDTOList.forEach(answerSheetTypeDTO ->
                answerSheetTypeDTO.getAnswerSheetList().forEach(answerSheetDTO -> {
                    if (idScoreMap.get(answerSheetDTO.getId()) != null) {
                        answerSheetDTO.setScore(idScoreMap.get(answerSheetDTO.getId()));
                    }
                })
        );
        // 计算试卷总得分
        List<AnswerSheetDTO> answerSheetDTOList = Lists.newArrayList();
        answerSheetTypeDTOList.forEach(dto -> answerSheetDTOList.addAll(dto.getAnswerSheetList()));
        int score = answerSheetDTOList.stream().map(AnswerSheetDTO::getScore).reduce(0, Integer::sum);

        answerSheet.setContent(JSONArray.toJSONString(answerSheetTypeDTOList));
        answerSheet.setPaperStatus(PaperStatusEnum.READ.getCode());
        answerSheet.setScore(score);
        answerSheet.setUpdateTime(DateTime.now());
        answerSheetService.updateAnswerSheet(answerSheet);

        if (answerSheetService.allChecked(answerSheet.getPaperId())) {
            CourseTestPaper paper = new CourseTestPaper();
            paper.setId(answerSheet.getPaperId());
            paper.setPaperStatus(PaperStatusEnum.READ.getCode());
            updatePaper(paper);
        }

    }

    @Override
    public List<Long> queryTestingCourseIds(List<Long> courseIds) {
        CourseTestPaperExample example = new CourseTestPaperExample();
        example.createCriteria().andCourseIdIn(courseIds).andPaperStatusEqualTo(PaperStatusEnum.TESTING.getCode());
        List<CourseTestPaper> data = paperMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(data)) {
            return Lists.newArrayList();
        }
        return data.stream().map(CourseTestPaper::getCourseId).distinct().collect(Collectors.toList());
    }

    @Override
    public void deletePaper(List<Long> courseIds) {
        // 删除试卷
        deleteByCourseIds(courseIds);

        // 回收脏数据
        cleanData();
    }

    @Override
    public void deletePaper(Long courseId, List<Long> classIds) {
        // 删除试卷
        deleteByCourseIdClassIds(courseId, classIds);

        // 回收脏数据
        cleanData();
    }

    private void deleteByCourseIdClassIds(Long courseId, List<Long> classIds) {
        if (CollectionUtils.isEmpty(classIds)) {
            return;
        }
        CourseTestPaperExample example = new CourseTestPaperExample();
        example.createCriteria().andCourseIdEqualTo(courseId);
        List<CourseTestPaper> courseTestPapers = paperMapper.selectByExample(example);
        // 删除中间表
        List<Long> paperIds = courseTestPapers.stream().map(CourseTestPaper::getId).collect(Collectors.toList());
        List<Long> delPaperIds = courseTestPaperClassService.delClassAndGetDelPaperIds(paperIds, classIds);

        // 班级下的学生ID
        List<UserClass> userClassList = userClassService.getByClassIds(classIds);
        List<Long> studentIds = userClassList.stream().map(UserClass::getUserId).collect(Collectors.toList());

        // 删除试卷
        CourseTestPaperExample courseTestPaperExample = new CourseTestPaperExample();
        courseTestPaperExample.createCriteria().andIdIn(delPaperIds);
        paperMapper.deleteByExample(courseTestPaperExample);

        //
        CourseTestPaperAnswerSheetExample delExample = new CourseTestPaperAnswerSheetExample();
        delExample.createCriteria().andPaperIdIn(delPaperIds).andStudentIdIn(studentIds);
        courseTestPaperAnswerSheetMapper.deleteByExample(delExample);

        paperMapper.deleteByExample(example);
    }

    private void cleanData() {
        // 回收题目脏数据
        questionService.cleanData();
        // 回收答题卡脏数据
        answerSheetService.cleanData();
        // 回收答案脏数据
        answerService.cleanData();
        // 回收实验题关联脏数据
        answerSheetExperimentService.cleanData();
    }

    @Override
    public List<CourseTestPaper> getTestingPaper() {
        CourseTestPaperExample example = new CourseTestPaperExample();
        example.createCriteria().andEndTimeGreaterThan(DateTime.now());
        return paperMapper.selectByExample(example);
    }

    @Override
    public List<CourseTestPaper> selectByCourseIds(List<Long> courseIds) {
        CourseTestPaperExample example = new CourseTestPaperExample();
        example.createCriteria().andCourseIdIn(courseIds);
        return paperMapper.selectByExample(example);
    }

    @Override
    public PageDTO<AnswerSheetListDTO> studentList(Integer pageNum, Integer pageSize, String userInfo, Long paperId) {
        if (!isPaperChecked(paperId)) {
            throw new GlobalException("系统正在自动批阅客观题，请稍后再进入");
        }

        PageHelper.startPage(pageNum, pageSize);
        List<AnswerSheetListDTO> list = answerSheetService.selectStudentList(userInfo, paperId);
        list.forEach(dto -> dto.setStatusMessage(StudentStatusEnum.forCode(dto.getPaperStatus()).getCheckMessage()));

        PageInfo<AnswerSheetListDTO> pageInfo = new PageInfo<>(list);

        return new PageDTO<>(pageInfo);
    }

    private boolean isPaperChecked(Long paperId) {
        CourseTestPaper paper = selectByPrimaryKey(paperId);

        long millisecond = DateUtil.between(DateTime.now(), paper.getEndTime(), DateUnit.MS, true);

        // 取批阅延时的1.5倍做时间限制
        float rate = 1.5f;
        return millisecond > rate * CourseTestConstant.AUTO_CHECK_DELAY;
    }

    @Override
    public PaperReadingDTO studentDetails(Long answerSheetId) {
        // 查询答题卡基本信息
        CourseTestPaperAnswerSheet answerSheet = answerSheetService.selectByPrimaryKey(answerSheetId);

        return getPaperReadingDTO(answerSheet);
    }


    private PaperReadingDTO getPaperReadingDTO(CourseTestPaperAnswerSheet answerSheet) {
        if (answerSheet == null) {
            throw new GlobalException("学生答题卡丢失!");
        }

        List<CourseTestPaperAnswerSheet> answerSheetList = answerSheetService.selectEndedPaperByPaperId(answerSheet.getPaperId());

        CourseTestPaper paper = selectPaperById(answerSheet.getPaperId());

        CourseBasicDTO course = courseService.getCourse(paper.getCourseId());

        UserInfoDTO user = userService.getUserInfo(answerSheet.getStudentId(), null);

        // 查询试卷题目信息
        List<CourseTestPaperQuestion> questionList = questionService.selectByPaperId(answerSheet.getPaperId());
        questionList.forEach(question -> question.setContent(question.getHtmlContent()));

        // 查询题目选项及答案信息
        List<Long> questionIds = questionList.stream().map(CourseTestPaperQuestion::getId).collect(Collectors.toList());
        List<CourseTestPaperAnswer> answerList = answerService.selectAnswerList(questionIds, answerSheet.getPaperId());

        // 转化答题列表
        List<AnswerSheetTypeDTO> answerSheetTypeDTOList = JSONArray.parseArray(answerSheet.getContent(), AnswerSheetTypeDTO.class);
        List<AnswerSheetDTO> answerSheetDTOList = AnswerSheetDTO.parse(answerSheetTypeDTOList);
        // 转化简答题的答案
        answerSheetDTOList.forEach(answer -> {
            if (QuestionTypeEnum.isSAQ(answer.getType()) && answer.getAnswer() != null) {
                answer.setAnswer(MDUtil.markdown2Html(answer.getAnswer().toString()));
            }
        });

        // 查询所有实验题的实验报告
        List<Long> experimentQuestionIds = answerSheetDTOList.stream()
                .filter(question -> Objects.equal(question.getType(), QuestionTypeEnum.EXPERIMENT.getCode()))
                .map(AnswerSheetDTO::getId).collect(Collectors.toList());
        List<AnswerSheetReport> answerSheetReportList = answerSheetExperimentService.selectByQuestionIds(experimentQuestionIds);

        List<Long> reportIds = answerSheetReportList.stream().map(AnswerSheetReport::getReportId).collect(Collectors.toList());
        List<UserPaperExperimentReportDTO> experimentReportList = experimentService.selectByIdsExt(reportIds);

        // 提取用户实验报告
        Map<Long, UserPaperExperimentReportDTO> userReports = experimentReportList.stream()
                .filter(report -> Objects.equal(report.getAnswerSheetId(), answerSheet.getId()))
                .collect(Collectors.toMap(UserPaperExperimentReportDTO::getQuestionId, obj -> obj, (k1, k2) -> k1));

        // 实验报告查重情况
        Map<Long, List<DuplicateResultDTO>> questionDuplicateMap;
        if (paper.getCheckReportDuplicate() && paper.getReportDuplicatePercent() < 100) {
            questionDuplicateMap = experimentService.mapQuestionDuplicate(answerSheet, experimentReportList, paper);
        } else {
            questionDuplicateMap = Maps.newHashMap();
        }

        // 提取题型-得分映射关系
        Map<Integer, Integer> typeScoreMap = questionList.stream().collect(
                Collectors.toMap(CourseTestPaperQuestion::getType, CourseTestPaperQuestion::getScore, (oldVal, currVal) -> oldVal)
        );

        return new PaperReadingDTO().convert(
                answerSheet, questionList, answerList, answerSheetList, paper, course, user, typeScoreMap,
                answerSheetDTOList, questionDuplicateMap, userReports
        );
    }

    @Override
    public void download(Long paperId,String className,String paperName) {
        List<CourseTestPaperAnswerSheet> courseTestPaperAnswerSheets = courseTestPaperAnswerSheetMapper.selectByPrimaryKeyOther(paperId);
        List<Map<String, Object>>list=Lists.newArrayList();
        Map<String, Object> map1=new HashMap();
        map1.put("sheetName", "考试详情信息");
        list.add(map1);
        courseTestPaperAnswerSheets.stream().forEach(courseTestPaperAnswerSheet->{
            Map<String, Object> map = new HashMap();

            List<User> users = userService.listUser(Lists.newArrayList(courseTestPaperAnswerSheet.getStudentId()));
            if (users != null && users.size() > 0) {
                List<AnswerSheetTypeDTO> answerSheetTypeDTOList = JSONArray.parseArray(courseTestPaperAnswerSheet.getContent(), AnswerSheetTypeDTO.class);
                if (answerSheetTypeDTOList != null) {
                    answerSheetTypeDTOList.stream().forEach(
                            answerSheetTypeDTO -> {
                                switch (answerSheetTypeDTO.getType()) {
                                    case 1:
                                        Integer sum=0;
                                        for (AnswerSheetDTO answerSheetDTO:answerSheetTypeDTO.getAnswerSheetList() ) {
                                            sum+=answerSheetDTO.getScore();
                                        }
                                        map.put("xzscore", sum);

                                        break;
                                    case 2:
                                        Integer sum1=0;
                                        for (AnswerSheetDTO answerSheetDTO:answerSheetTypeDTO.getAnswerSheetList() ) {
                                            sum1+=answerSheetDTO.getScore();
                                        }
                                        map.put("pdscore", sum1);

                                        break;
                                    case 3:
                                        Integer sum2=0;
                                        for (AnswerSheetDTO answerSheetDTO:answerSheetTypeDTO.getAnswerSheetList() ) {
                                            sum2+=answerSheetDTO.getScore();
                                        }
                                        map.put("tkscore", sum2);

                                        break;
                                    case 4:
                                        Integer sum3=0;
                                        for (AnswerSheetDTO answerSheetDTO:answerSheetTypeDTO.getAnswerSheetList() ) {
                                            sum3+=answerSheetDTO.getScore();
                                        }
                                        map.put("jdscore", sum3);

                                        break;
                                    case 6:
                                        Integer sum4=0;
                                        for (AnswerSheetDTO answerSheetDTO:answerSheetTypeDTO.getAnswerSheetList() ) {
                                            sum4+=answerSheetDTO.getScore();
                                        }
                                        map.put("syscore", sum4);

                                        break;
                                }

                            }

                    );
                }
                map.put("name", users.get(0).getName().toString());
                map.put("userNum", users.get(0).getUserNumber().toString());
                map.put("score",courseTestPaperAnswerSheet.getScore());
                list.add(map);
            }
        });
        if (list.size()==1) throw new GlobalException("当前考试人员已经被清除");
        String[] columnNames = {"学生姓名", "学生学号", "考试成绩","选择题得分","判断题得分","填空题得分","简答题得分","实验题得分"};
        String[] keys = {"name", "userNum", "score","xzscore","pdscore","tkscore","jdscore","syscore"};
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        ExcelsUtil.ExcelSingleOutputStreamExperimentPaper( paperName, className, list, keys, columnNames, "paper", request, response);

    }

    @Override
    public void endPaper(Long id) {
        CourseTestPaper paper = new CourseTestPaper();
        paper.setId(id);
        paper.setPaperStatus(answerSheetService.allChecked(id) ? PaperStatusEnum.READ.getCode() : PaperStatusEnum.READING.getCode());
        updatePaper(paper);
    }

    @Override
    public List<CourseClassDTO> getCourseClassPaper(List<CourseClassDTO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }
        List<CourseTestPaper> paperList = paperMapperExt.selectByCourseIdClassIdList(list);
        return BeanUtil.batchTransform(CourseClassDTO.class, paperList);
    }

    @Override
    public Class getDefaultClass(Long courseId) {
        Class classInfo = classService.getDefaultClass(courseId);
        if (classInfo == null) {
            throw new GlobalException("当前课程没有默认班级");
        }
        return classInfo;
    }

    @Override
    public TeacherPaperDTO previewPaper(Long courseId, PreviewPaperParam param) {
        Course course = courseService.getCourseById(courseId);
        Long userId = course.getUserId();

        // 如果选定题目列表为空，则需要后台随机选题
        if (CollectionUtils.isEmpty(param.getQuestionList())) {
            if (param.getModelId() != null) {
                param.setQuestionList(randomQuestion(courseId, param.getModelId(), userId));
            } else {
                // 抛出异常
                throw new GlobalException("题目列表和模板id不可同时为空");
            }
        }
        return generatePreviewPaper(param);
    }

    @Override
    @Transactional
    public void savePaper(Long courseId, PaperParam param) {
        Course course = courseService.getCourseById(courseId);
        Long userId = course.getUserId();

        if (existPaper(courseId, userId, param.getPaperName())) {
            throw new GlobalException("试卷名称重复");
        }
        // 插入试卷基本信息
        CourseTestPaper paper = insertPaper(courseId, param, userId);
        // 插入试卷题目内容
        questionService.insertPaperQuestion(paper.getId(), param.getContent());
    }

    private boolean existPaper(Long courseId, Long userId, String paperName) {
        CourseTestPaperExample example = new CourseTestPaperExample();
        example.createCriteria().andTeacherIdEqualTo(userId).andCourseIdEqualTo(courseId).andPaperNameEqualTo(paperName);
        return paperMapper.countByExample(example) > 0;
    }

    @Override
    public CourseTestPaper selectPaperById(Long id) {
        return paperMapper.selectByPrimaryKey(id);
    }

    /**
     * 应用于教师查看试卷方法内
     *
     * @param questionListTypeMap     题型-题目列表映射
     * @param choiceListQuestionIdMap 题目id-选择题选项映射
     * @return 题型-题目详情列表
     */
    private List<PaperTypeDTO> convert2PaperTypeDTO(
            Map<Integer, List<CourseTestPaperQuestion>> questionListTypeMap,
            Map<Long, List<CourseTestPaperAnswer>> choiceListQuestionIdMap) {
        List<PaperTypeDTO> result = Lists.newArrayList();
        for (Map.Entry<Integer, List<CourseTestPaperQuestion>> entry : questionListTypeMap.entrySet()) {
            Integer type = entry.getKey();
            List<CourseTestPaperQuestion> questionList = questionListTypeMap.get(type);

            PaperTypeDTO dto = new PaperTypeDTO();
            // 设置题型
            dto.setType(type);
            // 设置题型分数
            dto.setScore(questionList.get(0).getScore());

            // 填充题干描述信息和答案信息
            List<QuestionDTO> questionDTOList = Lists.newArrayList();
            for (CourseTestPaperQuestion question : questionList) {
                questionDTOList.add(new QuestionDTO(question, type, choiceListQuestionIdMap.get(question.getId())));
            }
            dto.setContent(questionDTOList);
            result.add(dto);
        }
        return result;
    }

    /**
     * 应用于预览试题方法内
     *
     * @param questionListTypeMap     题型-题目列表映射
     * @param typeScoreMap            题型-分数映射
     * @param choiceListQuestionIdMap 题目id-选择题选项映射
     * @return 题型-题目详情列表
     */
    private List<PaperTypeDTO> convert2PaperTypeDTO(
            Map<Integer, List<CourseQuestionRepository>> questionListTypeMap,
            Map<Integer, PreviewQuestionParam> typeScoreMap,
            Map<Long, List<CourseQuestionRepositoryDetails>> choiceListQuestionIdMap) {
        List<PaperTypeDTO> result = Lists.newArrayList();
        int orderNumber = 0;

        for (Map.Entry<Integer, List<CourseQuestionRepository>> entry : questionListTypeMap.entrySet()) {
            Integer type = entry.getKey();
            List<CourseQuestionRepository> questionList = questionListTypeMap.get(type);

            PaperTypeDTO dto = new PaperTypeDTO();
            // 设置题型
            dto.setType(type);
            // 设置题型分数
            dto.setScore(typeScoreMap.get(type).getScore());

            // 填充题干描述信息和答案信息
            List<QuestionDTO> questionDTOList = Lists.newArrayList();
            for (CourseQuestionRepository question : questionList) {
                QuestionDTO obj = new QuestionDTO(question, orderNumber++, type, choiceListQuestionIdMap.get(question.getId()));
                questionDTOList.add(obj);
            }
            dto.setContent(questionDTOList);
            result.add(dto);
        }
        return result;
    }

    private Map<Long, List<CourseQuestionRepositoryDetails>> getPreviewChoiceList(
            List<CourseQuestionRepository> choiceQuestionList) {
        if (CollectionUtils.isEmpty(choiceQuestionList)) {
            return Maps.newHashMap();
        }
        List<Long> choiceQuestionIdList = choiceQuestionList.stream().map(
                CourseQuestionRepository::getId
        ).collect(Collectors.toList());
        // 获取选定题目的答案详情映射
        return questionRepositoryDetailsService.getDetailsMap(choiceQuestionIdList, FileTypeEnum.HTML);
    }

    private List<CourseQuestionRepository> getPreviewQuestionList(List<PreviewQuestionParam> questionParamList) {
        // 汇总全部题目id
        List<Long> sourceQuestionIds = Lists.newArrayList();
        questionParamList.forEach(param -> sourceQuestionIds.addAll(param.getQuestionIdList()));
        // 根据题目id查询对应全部题干
        List<CourseQuestionRepository> questionList = questionRepositoryService.selectQuestionByIds(sourceQuestionIds);
        // 反显html字符串
        questionList.forEach(repository -> repository.setContent(repository.getHtmlContent()));
        return questionList;
    }

    /**
     * @param userId     用户id
     * @param paperId    试卷id
     * @param questionId 题目id
     * @return 学生端查看试卷页面数据
     */
    private StudentPaperDTO studentPaperDetails(Long userId, Long paperId, Long questionId, Long courseId) {
        CourseTestPaper paper = selectPaperById(paperId);
        List<Long> classIdsOfPaperId = courseTestPaperClassService.getClassByPaperId(Lists.newArrayList(paperId));

        if (paper == null) {
            throw new GlobalException("试卷不存在,请退出");
        }

        List<Long> classIds = classService.queryStudentClass(courseId, null);
        if (CollectionUtils.isEmpty(classIds)) {
            throw new GlobalException("您无法上该课程,请联系授课教师");
        }

        if (classIdsOfPaperId.contains(classIds.get(0)) == false) {
            throw new GlobalException("没有该项考试,请退出考试页面");
        }

        CourseTestPaperAnswerSheet answerSheet = answerSheetService.selectInsert(userId, paperId);

        List<CourseTestPaperQuestion> questionList = questionService.selectByPaperId(paperId);
        questionList.sort(Comparator.comparing(CourseTestPaperQuestion::getOrderNumber));

        if (CollectionUtils.isEmpty(questionList)) {
            throw new GlobalException("当前试卷中没有题目数据");
        }

        Long timeLeft = getTimeLeft(paperId);

        if (timeLeft <= 0) {

//            answerSheetService.asyncEndTest(answerSheet.getId(), paperId);
            throw new GlobalException("考试已结束");
        }

        AnswerSheetStatusEnum statusEnum = AnswerSheetStatusEnum.forCode(answerSheet.getPaperStatus());
        if (statusEnum != AnswerSheetStatusEnum.EXPERIMENT_TEST && QuestionTypeEnum.isExperiment(questionList.get(0).getType())) {
            statusEnum = AnswerSheetStatusEnum.EXPERIMENT_TEST;
        }

        return statusEnum.paperDetails(userId, paper, paperId, questionId, answerSheet, questionList, courseId, timeLeft);
    }

    /**
     * 变更考试状态
     *
     * @param param       开始考试参数
     * @param paperStatus 试卷状态
     * @param timeLength  考试时长
     */
    private void updatePaperInfo(CourseTestPaper paper, List<Long> classIds,TestStartParam param, Integer paperStatus, Integer timeLength) {

        paper.setCheckReportDuplicate(param.getCheckReportDuplicate());
        paper.setReportDuplicatePercent(param.getReportDuplicatePercent());
        paper.setPaperStatus(paperStatus);
        paper.setEndTime(paper.getEndTimeOrDate());
        paper.setUpdateTime(DateTime.now());
        paperMapper.updateByPrimaryKeySelective(paper);

        // 执行中间表插入
        Long paperId = paper.getId();
        for (int i = 0; i < classIds.size(); i++) {
            Long classId = classIds.get(i);
            CourseTestPaperClass courseTestPaperClass = new CourseTestPaperClass();
            courseTestPaperClass.setPaperId(paperId);
            courseTestPaperClass.setClassId(classId);
            courseTestPaperClassMapper.insert(courseTestPaperClass);
        }
    }

    private void deletePaperInfo(Long paperId) {
        // 删除答题卡信息
        answerSheetService.deleteByPaperId(paperId);
        // 删除试卷答案信息
        answerService.deleteByPaperId(paperId);
        // 删除试卷题目信息
        questionService.deleteByPaperId(paperId);
        // 删除试卷信息
        paperMapper.deleteByPrimaryKey(paperId);
    }

    /**
     * 根据课程id删除试卷
     */
    private void deleteByCourseIds(List<Long> courseIds) {
        CourseTestPaperExample example = new CourseTestPaperExample();
        example.createCriteria().andCourseIdIn(courseIds);
        paperMapper.deleteByExample(example);
    }

    private List<AnswerSheetTypeDTO> convert2AnswerSheetTypeDTO(List<TypeReadingDTO> typeReadingList) {
        List<AnswerSheetDTO> answerSheetList = Lists.newArrayList();
        typeReadingList.forEach(dto -> answerSheetList.addAll(BeanUtil.batchTransform(AnswerSheetDTO.class, dto.getQuestionList())));
        return AnswerSheetTypeDTO.transform(answerSheetList);
    }

    private CourseTestPaper insertPaper(Long courseId, PaperParam param, Long userId) {
        CourseTestPaper paper = new CourseTestPaper();
        paper.setPaperName(param.getPaperName());
        paper.setTimeLength(param.getTimeLength());
        paper.setScore(param.getScore());
        paper.setCourseId(courseId);
        paper.setTeacherId(userId);
        paper.setEndType(param.getEndType());
        paper.setEndDate(param.getEndDate());
        paperMapper.insertSelective(paper);
        return paper;
    }

    /**
     * @param courseId 课程id
     * @param modelId  模板id
     * @param userId   用户id
     * @return 根据模板返回随机选题结果
     */
    private List<PreviewQuestionParam> randomQuestion(Long courseId, Long modelId, Long userId) {
        List<PreviewQuestionParam> result = Lists.newArrayList();

        // 获取试卷模板
        CourseTestPaperModel model = paperModelService.selectByPrimaryKey(modelId);

        // 获取当前教师选定课程全部题目
        List<CourseQuestionRepository> userQuestionList = questionRepositoryService.getUserQuestionList(courseId, userId, QuestionBusinessTypeEnum.TEST.getCode());

        if (CollectionUtils.isEmpty(userQuestionList)) {
            throw new GlobalException("课程下没有题目");
        }

        // 过滤不可见章节
        userQuestionList = filterFalg(userQuestionList, courseId);

        // 获取题目的答案信息
        if (model.getIsChapterModel()) {
            Map<Long, List<CourseQuestionRepository>> userQuestionListChapterIdMap = BeanUtil.aggByKeyToList("chapterId", userQuestionList);
            List<ModelIncludeChapterDTO> chapterModelList = JSONArray.parseArray(model.getContent(), ModelIncludeChapterDTO.class);
            for (ModelIncludeChapterDTO dto : chapterModelList) {
                List<ModelNotChapterDTO> modelList = dto.getContent();
                modelList.forEach(modelDTO -> result.add(randomChoose(userQuestionListChapterIdMap.get(dto.getId()), modelDTO)));
            }
        } else {
            List<ModelNotChapterDTO> modelList = JSONArray.parseArray(model.getContent(), ModelNotChapterDTO.class);
            List<CourseQuestionRepository> finalUserQuestionList = userQuestionList;
            modelList.forEach(
                    modelDTO -> result.add(randomChoose(finalUserQuestionList, modelDTO))
            );
        }

        return combineUserQuestionList(result);
    }

    private List<CourseQuestionRepository> filterFalg(List<CourseQuestionRepository> userQuestionList, Long courseId) {
        List<ChapterBasic> chapterList = chapterService.getChapterBasic(courseId, null, null, null);
        List<Long> chapterIds = chapterList.stream().filter(chapterBasic -> chapterBasic.getFalg() == 0).map(ChapterBasic::getId).collect(Collectors.toList());

        return userQuestionList.stream().filter(courseQuestionRepository -> chapterIds.contains(courseQuestionRepository.getChapterId()) || courseQuestionRepository.getChapterId() == null).collect(Collectors.toList());
    }

    private List<PreviewQuestionParam> combineUserQuestionList(List<PreviewQuestionParam> paramList) {
        List<PreviewQuestionParam> result = Lists.newArrayList();

        Map<Integer, List<PreviewQuestionParam>> paramListTypeMap = BeanUtil.aggByKeyToList("type", paramList);
        paramListTypeMap.forEach((type, list) -> {
            PreviewQuestionParam param = new PreviewQuestionParam();
            param.setType(type);
            param.setScore(list.get(0).getScore());

            List<Long> questionIds = Lists.newArrayList();
            list.stream().map(PreviewQuestionParam::getQuestionIdList).forEach(questionIds::addAll);
            param.setQuestionIdList(questionIds);

            result.add(param);
        });

        return result;
    }

    /**
     * @param questionRepositoryList 当前全部的题库列表
     * @param dto                    模板
     * @return 按照模板随机选题
     */
    private PreviewQuestionParam randomChoose(
            List<CourseQuestionRepository> questionRepositoryList, ModelNotChapterDTO dto) {
        PreviewQuestionParam param = BeanUtil.transform(PreviewQuestionParam.class, dto);
        if (param == null) {
            return null;
        }

        Map<Integer, List<CourseQuestionRepository>> questionRepositoryListTypeMap =
                BeanUtil.aggByKeyToList("questionType", questionRepositoryList);

        Set<Long> questionIds = Sets.newHashSet();

        do {
            List<CourseQuestionRepository> questionList = questionRepositoryListTypeMap.get(dto.getType());
            if (CollectionUtils.isEmpty(questionList) || questionList.size() < dto.getCount()) {
                throw new GlobalException("题目数量不足以进行随机选题");
            }
            questionIds.add(RandomUtil.randomEle(questionList).getId());
        } while (questionIds.size() < dto.getCount());

        param.setQuestionIdList(Lists.newArrayList(questionIds));
        return param;
    }


    /**
     * @param param 试卷预览参数
     * @return 预览试卷页面数据
     */
    private TeacherPaperDTO generatePreviewPaper(PreviewPaperParam param) {
        TeacherPaperDTO teacherPaperDTO = BeanUtil.transform(TeacherPaperDTO.class, param);
        if (teacherPaperDTO == null) {
            return null;
        }

        // 获取选定题目的题干详情
        List<CourseQuestionRepository> questionList = getPreviewQuestionList(param.getQuestionList());

        // 按照题型分类试题描述
        Map<Integer, List<CourseQuestionRepository>> questionListTypeMap =
                BeanUtil.aggByKeyToList("questionType", questionList);

        // 获取选择题选项并做（questionId-选项）映射

        Map<Long, List<CourseQuestionRepositoryDetails>> choiceListQuestionIdMap =
                getPreviewChoiceList(questionListTypeMap.get(QuestionTypeEnum.CHOICE.getCode()));

        // 映射题型、题目参数，用于稍后回填题型分数
        Map<Integer, PreviewQuestionParam> typeScoreMap = BeanUtil.mapByKey("type", param.getQuestionList());
        // 封装题干描述，答案描述信息
        teacherPaperDTO.setContent(convert2PaperTypeDTO(questionListTypeMap, typeScoreMap, choiceListQuestionIdMap));
        // 封装分数信息
        teacherPaperDTO.setScore(computePreviewScore(param));


        return teacherPaperDTO;
    }

    /**
     * @param param 模板或者题型分数关系
     * @return 试卷总分
     */
    private Integer computePreviewScore(PreviewPaperParam param) {
        // 如果选定题目列表为空，则需要后台随机选题
        if (CollectionUtils.isEmpty(param.getQuestionList())) {
            if (param.getModelId() != null) {
                return paperModelService.selectByPrimaryKey(param.getModelId()).getScore();
            } else {
                // 抛出异常
                throw new GlobalException("题目列表和模板id不可同时为空");
            }
        }

        // 从体型-分数关系中核算总分
        int score = 0;
        for (PreviewQuestionParam typeScore : param.getQuestionList()) {
            score += typeScore.getScore() * typeScore.getQuestionIdList().size();
        }

        return score;
    }

    /**
     * @param userInfo 学生姓名
     * @param paperId 试卷ID
     */
	@Override
	public String download(String userInfo, Long paperId) {

        boolean isHavePackage = papaerIsHavePackage.containsKey(paperId);
        if(isHavePackage){
            String httpZipPath = papaerIsHavePackage.get(paperId);
            return httpZipPath;
        }else{
            // 已经进入 直接返回
            String lastValue = lockQuee.putIfAbsent(paperId, "LOCK");
            if(StrUtil.isBlank(lastValue)){
                Console.log("=====进入返回页====");
                return "";
            }
            try {
                threadPoolTaskExecutor.execute(()->{

                    String className = null;
                    try {
                        String basePath =  LocalResourcesEnum.REPORT.getLocalUploadPath() + File.separator + paperId;
                        List<AnswerSheetListDTO> list = answerSheetService.selectStudentList(userInfo, paperId);
                        if( list == null ) {
                            list = Lists.newArrayList();
                        }
                        if( CollUtil.isNotEmpty(list) ){
                            className = CollUtil.getFirst(list).getClassName();
                        }
                        list.forEach(dto -> dto.setStatusMessage(StudentStatusEnum.forCode(dto.getPaperStatus()).getCheckMessage()));
                        List<Map<String, Object>> excelList = new ArrayList<>();
                        Map<String, Object> header = new HashMap<>();
                        header.put("sheetName", "考试成绩统计");
                        excelList.add(header);

                        for (int i = 0; i < list.size(); i++) {

                            Map<String, Object> line = new HashMap<>();
                            line.put("index", i+1);
                            line.put("studentNumber", list.get(i).getStudentNumber());
                            line.put("studentName", list.get(i).getStudentName());
                            line.put("className", list.get(i).getClassName());
                            line.put("grade", list.get(i).getGrade());
                            line.put("score",list.get(i).getScore());
                            line.put("statusMessage", list.get(i).getStatusMessage());
                            excelList.add(line);
                        }

                        String[] columnNames = {"序号", "学生姓名", "学号", "班级", "年级", "得分", "状态"};
                        String[] keys = {"index", "studentName", "studentNumber",  "className", "grade", "score", "statusMessage"};
                        File excel = new File(basePath + File.separator + className +"考试成绩.xls");
                        FileUtil.touch(excel);
                        try {
                            FileOutputStream fileOutputStream = new FileOutputStream(excel);
                            ExcelsUtil.createSingleWorkBook(new HSSFWorkbook(), excelList, keys, columnNames).write(fileOutputStream);
                            fileOutputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        // 1. 生成excel之后生成考试文件
                        List<AnswerSheetExprimentInfoDTO> exprimentInfoList = answerSheetService.getExperimentInfo(paperId);
                        List<CourseTestPaperQuestion> courseTestPaperQuestions = questionService.selectByPaperId(paperId);
                        Map<Long, List<CourseTestPaperQuestion>> questionMap = courseTestPaperQuestions.stream().collect(Collectors.groupingBy(CourseTestPaperQuestion::getId));

                        // 2. 生成文件
                        for (AnswerSheetExprimentInfoDTO answerSheetExprimentInfoDTO : exprimentInfoList) {
                            // 获取当前题目题号
                            Integer number;
                            List<CourseTestPaperQuestion> courseTestPaperNUmbers = questionMap.get(answerSheetExprimentInfoDTO.getQuestionId());
                            if(CollectionUtil.isEmpty(courseTestPaperNUmbers)){
                                continue;
                            }else{
                                number = courseTestPaperNUmbers.get(0).getOrderNumber();
                            }

                            String htmlContent = "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">"
                                    + "\n"+answerSheetExprimentInfoDTO.getReportHtml();

                            // -------补录pdf中没有图片问题 start 补录数据完成后可将此逻辑删除-------
                            Document doc = Jsoup.parse(htmlContent);
                            Elements imgs = doc.getElementsByTag("img");
                            // 获取当前服务的
                            for (Element img : imgs) {
                                String resourceSrc = img.attr("src");
                                if(resourceSrc.contains(localMappeing) == false){
                                    img.attr("src", localMappeing + resourceSrc);
                                    logger.debug("=========打印图片的全路径===========" + localMappeing + resourceSrc);
                                    Console.log("=========打印图片的全路径===========" + localMappeing + resourceSrc);
                                }
                            }
                            htmlContent = doc.html();
                            logger.debug(htmlContent);
                            // -------补录pdf中没有图片问题 end -------
                            String fileName = answerSheetExprimentInfoDTO.getName()+"_"+answerSheetExprimentInfoDTO.getUserNumber();
                            String basePathDir = basePath + File.separator +"题目_"+number + File.separator + fileName + File.separator;
                            if(StrUtil.isNotBlank(htmlContent)){
                                String pdfPath = basePathDir + fileName+ ".pdf";
                                fileService.html2pdf(pdfPath,htmlContent);
                                logger.debug(htmlContent);
                            }
                            String reportFile = answerSheetExprimentInfoDTO.getReportFile();

                            if(StrUtil.isNotBlank(reportFile)){
                                String filePath = LocalResourcesEnum.REPORT.getLocalUploadPath(reportFile);
                                File file = new File(filePath);
                                if(file.exists()){
                                    FileUtil.copy(filePath, basePathDir + answerSheetExprimentInfoDTO.getReportFileName(), true);
                                }
                            }
                        }
                        // zip 文件夹打包进行文件传输
                        File zip = ZipUtil.zip(basePath, LocalResourcesEnum.REPORT.getLocalUploadPath() +File.separator+ className+"_"+paperId+".zip");
                        // 进行路径替换
                        papaerIsHavePackage.put(paperId, LocalResourcesEnum.REPORT.getMappingUrl(zip.getName()));
                    }catch(Exception e){
                        logger.error("========存在异常产生======", e);
                        Console.log("========存在异常产生======", e);
                    }
                    finally {
                        logger.error("======执行移除文件操作=====");
                        Console.log("======执行移除文件操作=====");
                        lockQuee.remove(paperId);
                    }
                });
            } catch (TaskRejectedException taskRejectedException){
                Console.log("生成文件失败",taskRejectedException);
                lockQuee.remove(paperId);
            } catch (Exception e){
                Console.log("生成文件失败",e);
                logger.error("生成文件失败", e);
                lockQuee.remove(paperId);
            }
            return "";
        }

    }

    @Override
    public String downloadPdfZip(String userInfo, Long paperId) {

        List<PaperReadingDTO> paperFullDTOs = getPaperFullDTOs(paperId, userInfo);
        String  zipPath = courseTestPaperTemplateService.createPaperInfoZip(paperFullDTOs);

        return zipPath;
    }

    @Override
    public PaperDetailDTO paperDetail(Long paperId) {

        CourseTestPaper courseTestPaper = paperMapper.selectByPrimaryKey(paperId);
        PaperDetailDTO paperDetailDTO = new PaperDetailDTO();
        paperDetailDTO.setPaperName(courseTestPaper.getPaperName());
        paperDetailDTO.setTimeLength(courseTestPaper.getTimeLength());
        List<PreviewQuestionParam> questionList = getQuestionLists(paperId);
        paperDetailDTO.setQuestionList(questionList);

        return paperDetailDTO;

    }

    private List<PreviewQuestionParam> getQuestionLists(Long paperId) {

        CourseTestPaperQuestionExample example = new CourseTestPaperQuestionExample();
        example.createCriteria().andPaperIdEqualTo(paperId);
        List<CourseTestPaperQuestion> courseTestPaperQuestions = courseTestPaperQuestionMapper.selectByExample(example);
        Map<Integer, List<CourseTestPaperQuestion>> typeMapCourseTestQuestions = courseTestPaperQuestions
                .stream()
                .collect(Collectors.groupingBy(CourseTestPaperQuestion::getType));
        // 题目类型
        List<PreviewQuestionParam> previewQuestionParams = new ArrayList<>();
        for (Integer type : typeMapCourseTestQuestions.keySet()) {

            PreviewQuestionParam previewQuestionParam = new PreviewQuestionParam();
            List<CourseTestPaperQuestion> typeMapCourseTestPaperQuestion = typeMapCourseTestQuestions.get(type);
            List<Long> questionIdList = typeMapCourseTestPaperQuestion.stream().map(CourseTestPaperQuestion::getOriginQuestionId).collect(Collectors.toList());
            previewQuestionParam.setQuestionIdList(questionIdList);
            previewQuestionParam.setType(type);
            previewQuestionParam.setScore(typeMapCourseTestPaperQuestion.get(0).getScore());
            previewQuestionParams.add(previewQuestionParam);
        }
        return previewQuestionParams;
    }

    /**
     * 获取试卷整体信息的paper
     * @param paperId
     * @param userInfo
     * @return
     */
    private List<PaperReadingDTO> getPaperFullDTOs(Long paperId, String userInfo) {

        List<AnswerSheetListDTO> list = answerSheetService.selectStudentList(userInfo, paperId);
        List<PaperReadingDTO> paperReadingDTOS = new ArrayList<>();
        list.forEach(dto -> dto.setStatusMessage(StudentStatusEnum.forCode(dto.getPaperStatus()).getCheckMessage()));
        // 查询答题卡基本信息
        List<Long> answerSheetListIds = list.stream().map(AnswerSheetListDTO::getId).collect(Collectors.toList());
        List<CourseTestPaperAnswerSheet> courseTestPaperAnswerSheets = answerSheetService.selectByPrimaryKeys(answerSheetListIds);
        Map<Long, List<CourseTestPaperAnswerSheet>> idMapCourseTestPaperAnswerSheet = courseTestPaperAnswerSheets.stream().collect(Collectors.groupingBy(CourseTestPaperAnswerSheet::getId));

        for (int i = 0; i < list.size() ; i++) {
            AnswerSheetListDTO answerSheetListDTO = list.get(i);
            Long id = answerSheetListDTO.getId();

            PaperReadingDTO paperReadingDTO = getPaperReadingDTO(idMapCourseTestPaperAnswerSheet.get(i).get(0));
            paperReadingDTOS.add(paperReadingDTO);
        }
        return paperReadingDTOS;
    }
}
